/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

#include "sdb.h"

#define NR_WP 32

typedef struct watchpoint {
    int NO;
    struct watchpoint *next;
    char* expr;
    word_t old_val;
} WP;

static WP wp_pool[NR_WP] = {};
static WP *head = NULL, *free_ = NULL;

void init_wp_pool() {
    int i;
    for (i = 0; i < NR_WP; i ++) {
        wp_pool[i].NO = i;
        wp_pool[i].next = (i == NR_WP - 1 ? NULL : &wp_pool[i + 1]);
    }

    head = NULL;
    free_ = wp_pool;
}

WP* new_wp() {
    if (free_ == NULL) return NULL;

    // Remove from free_
    WP* wp = free_;
    free_ = free_->next;
    // Add to head
    wp->next = head;
    head = wp;
    return wp;
}

void free_wp(WP *wp) {
    if (wp == NULL) return;

    free(wp->expr);
    // Add to free_
    wp->next = free_;
    free_ = wp;
}

int set_wp(char* e) {
    bool success;
    word_t val = expr(e, &success);
    if (success == false) return -1;

    WP* wp = new_wp();
    wp->expr = strdup(e);
    wp->old_val = val;

    return wp->NO;
}

bool del_wp(int NO) {
    WP* cur = head;
    WP* prev = NULL;
    while (cur != NULL) {
        if (cur->NO == NO) break;
        prev = cur;
        cur = cur->next;
    }

    // Not found, or head is NULL
    if (cur == NULL) {
        printf("Not found watchpoint [%d]\n", NO);
        return false;
    }
    // the first is target
    if (prev == NULL) {
        head = cur->next;
    }
    // the first is not target 
    else {
        // Remove from list
        prev->next = cur->next;
    }

    free_wp(cur);

    return true;
}

void list_wp() {
    if (head == NULL) {
        printf("No watchpoints\n");
        return;
    }
    WP* cur = head;
    while (cur != NULL) {
        printf("Watchpoint [%d] : expr [%s] \n", cur->NO, cur->expr);
        cur = cur->next;
    }
}

void check_wp(vaddr_t pc) {
    bool success;
    WP* cur = head;
    while (cur != NULL) {
        word_t new_val = expr(cur->expr, &success);
        if (new_val != cur->old_val) {
            printf("Watchpoint [%d] at " FMT_WORD ": %s\n", cur->NO, pc, cur->expr);
            printf("old value: " FMT_WORD ", " "new value: " FMT_WORD "\n", cur->old_val, new_val);
            cur->old_val = new_val;
            nemu_state.state = NEMU_STOP;
            return;
        }
        cur = cur->next;
    }
}
